{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "826IBSWMN4rr"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "ITj3u97-tNR7"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BYwfpc4wN4rt"
      },
      "source": [
        "# 重みクラスタリングの総合ガイド"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IFva_Ed5N4ru"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/model_optimization/guide/clustering/clustering_comprehensive_guide\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\"> TensorFlow.orgで表示</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ja/model_optimization/guide/clustering/clustering_comprehensive_guide.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\"> Google Colab で実行</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ja/model_optimization/guide/clustering/clustering_comprehensive_guide.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub でソースを表示{</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ja/model_optimization/guide/clustering/clustering_comprehensive_guide.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">ノートブックをダウンロード/a0}</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tidmcl3sN4rv"
      },
      "source": [
        "TensorFlow Model Optimization ツールキットの一部である*重みクラスタリング*の総合ガイドへようこそ。\n",
        "\n",
        "このページでは、さまざまなユースケースを示し、それぞれで API を使用する方法を説明します。どの API が必要であるかを特定したら、[API ドキュメント](https://www.tensorflow.org/model_optimization/api_docs/python/tfmot/clustering)でパラメータと詳細を確認してください。\n",
        "\n",
        "- 重みクラスタリングのメリットとサポート対象を確認する場合は、[概要](https://www.tensorflow.org/model_optimization/guide/clustering)をご覧ください。\n",
        "- 単一のエンドツーエンドの例については、[重みクラスタリングの例](https://www.tensorflow.org/model_optimization/guide/clustering/clustering_example)をご覧ください。\n",
        "\n",
        "このガイドでは、次のユースケースについて説明しています。\n",
        "\n",
        "- クラスタモデルを定義する\n",
        "- クラスタモデルのチェックポイントと逆シリアル化\n",
        "- クラスタモデルの精度を改善する\n",
        "- デプロイのみについて、ステップを実行して圧縮のメリットを確認する必要があります。\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RRtKxbo8N4rv"
      },
      "source": [
        "## セットアップ\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "08dJRvOqN4rw"
      },
      "outputs": [],
      "source": [
        "! pip install -q tensorflow-model-optimization\n",
        "\n",
        "import tensorflow as tf\n",
        "import numpy as np\n",
        "import tempfile\n",
        "import os\n",
        "import tensorflow_model_optimization as tfmot\n",
        "\n",
        "input_dim = 20\n",
        "output_dim = 20\n",
        "x_train = np.random.randn(1, input_dim).astype(np.float32)\n",
        "y_train = tf.keras.utils.to_categorical(np.random.randn(1), num_classes=output_dim)\n",
        "\n",
        "def setup_model():\n",
        "  model = tf.keras.Sequential([\n",
        "      tf.keras.layers.Dense(input_dim, input_shape=[input_dim]),\n",
        "      tf.keras.layers.Flatten()\n",
        "  ])\n",
        "  return model\n",
        "\n",
        "def train_model(model):\n",
        "  model.compile(\n",
        "      loss=tf.keras.losses.categorical_crossentropy,\n",
        "      optimizer='adam',\n",
        "      metrics=['accuracy']\n",
        "  )\n",
        "  model.summary()\n",
        "  model.fit(x_train, y_train)\n",
        "  return model\n",
        "\n",
        "def save_model_weights(model):\n",
        "  _, pretrained_weights = tempfile.mkstemp('.h5')\n",
        "  model.save_weights(pretrained_weights)\n",
        "  return pretrained_weights\n",
        "\n",
        "def setup_pretrained_weights():\n",
        "  model= setup_model()\n",
        "  model = train_model(model)\n",
        "  pretrained_weights = save_model_weights(model)\n",
        "  return pretrained_weights\n",
        "\n",
        "def setup_pretrained_model():\n",
        "  model = setup_model()\n",
        "  pretrained_weights = setup_pretrained_weights()\n",
        "  model.load_weights(pretrained_weights)\n",
        "  return model\n",
        "\n",
        "def save_model_file(model):\n",
        "  _, keras_file = tempfile.mkstemp('.h5') \n",
        "  model.save(keras_file, include_optimizer=False)\n",
        "  return keras_file\n",
        "\n",
        "def get_gzipped_model_size(model):\n",
        "  # It returns the size of the gzipped model in bytes.\n",
        "  import os\n",
        "  import zipfile\n",
        "\n",
        "  keras_file = save_model_file(model)\n",
        "\n",
        "  _, zipped_file = tempfile.mkstemp('.zip')\n",
        "  with zipfile.ZipFile(zipped_file, 'w', compression=zipfile.ZIP_DEFLATED) as f:\n",
        "    f.write(keras_file)\n",
        "  return os.path.getsize(zipped_file)\n",
        "\n",
        "setup_model()\n",
        "pretrained_weights = setup_pretrained_weights()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ARd37qONN4rz"
      },
      "source": [
        "## クラスタモデルを定義する\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zHB3pkU3N4r0"
      },
      "source": [
        "### モデル全体のクラスタリング（Sequential と Functional）"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ig-il1lmN4r1"
      },
      "source": [
        "モデルの精度を高めるための**ヒント**:\n",
        "\n",
        "- この API には許容できる精度のトレーニング済みモデルを渡す必要があります。クラスタリングを使用してモデルを最初からトレーニングすると、精度が低くなります。\n",
        "- 一部のケースでは、特定のレイヤーをクラスタリングすると、モデルの精度に悪影響が及びます。精度に最も大きく影響するレイヤーのクラスタリングを省略する方法について、「一部のレイヤーをクラスタリングする」をご覧ください。\n",
        "\n",
        "すべてのレイヤーをクラスタリングするには、モデルに `tfmot.clustering.keras.cluster_weights` を適用します。\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "29g7OADjN4r1"
      },
      "outputs": [],
      "source": [
        "import tensorflow_model_optimization as tfmot\n",
        "\n",
        "cluster_weights = tfmot.clustering.keras.cluster_weights\n",
        "CentroidInitialization = tfmot.clustering.keras.CentroidInitialization\n",
        "\n",
        "clustering_params = {\n",
        "  'number_of_clusters': 3,\n",
        "  'cluster_centroids_init': CentroidInitialization.DENSITY_BASED\n",
        "}\n",
        "\n",
        "model = setup_model()\n",
        "model.load_weights(pretrained_weights)\n",
        "\n",
        "clustered_model = cluster_weights(model, **clustering_params)\n",
        "\n",
        "clustered_model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zEOHK4OON4r7"
      },
      "source": [
        "### 一部のレイヤーをクラスタリングする（Sequential モデルと Functional モデル）\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ENscQ7ZWN4r8"
      },
      "source": [
        "モデルの精度を高めるための**ヒント**:\n",
        "\n",
        "- You must pass a pre-trained model with acceptable accuracy to this API. Training models from scratch with clustering results in subpar accuracy.\n",
        "- 初期のレイヤーと比較し、後のレイヤーはより多い冗長パラメータ（`tf.keras.layers.Dense`、`tf.keras.layers.Conv2D` など）でクラスタリングします。\n",
        "- 微調整中、クラスタリングレイヤーの前に初期のレイヤーを凍結します。凍結したレイヤーの数をハイパーパラメータとして処理します。経験的に、現在のクラスタリング API では、最も初期のレイヤーを凍結することが理想的です。\n",
        "- クリティカルレイヤー（注意メカニズムなど）のクラスリングを回避します。\n",
        "\n",
        "**その他**: `tfmot.clustering.keras.cluster_weights` API ドキュメントには、レイヤーごとにクラスタ構成を変える方法が示されています。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "IqBdl3uJN4r_"
      },
      "outputs": [],
      "source": [
        "# Create a base model\n",
        "base_model = setup_model()\n",
        "base_model.load_weights(pretrained_weights)\n",
        "\n",
        "# Helper function uses `cluster_weights` to make only \n",
        "# the Dense layers train with clustering\n",
        "def apply_clustering_to_dense(layer):\n",
        "  if isinstance(layer, tf.keras.layers.Dense):\n",
        "    return cluster_weights(layer, **clustering_params)\n",
        "  return layer\n",
        "\n",
        "# Use `tf.keras.models.clone_model` to apply `apply_clustering_to_dense` \n",
        "# to the layers of the model.\n",
        "clustered_model = tf.keras.models.clone_model(\n",
        "    base_model,\n",
        "    clone_function=apply_clustering_to_dense,\n",
        ")\n",
        "\n",
        "clustered_model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hN0DgpvD5Add"
      },
      "source": [
        "## モデルのチェックポイントと逆シリアル化"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hfji5KWN6XCF"
      },
      "source": [
        "**ユースケース:** このコードは、HDF5 モデル形式のみで必要です（HDF5 重みまたはその他の形式では不要です）。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "w7P67mPk6RkQ"
      },
      "outputs": [],
      "source": [
        "# Define the model.\n",
        "base_model = setup_model()\n",
        "base_model.load_weights(pretrained_weights)\n",
        "clustered_model = cluster_weights(base_model, **clustering_params)\n",
        "\n",
        "# Save or checkpoint the model.\n",
        "_, keras_model_file = tempfile.mkstemp('.h5')\n",
        "clustered_model.save(keras_model_file, include_optimizer=True)\n",
        "\n",
        "# `cluster_scope` is needed for deserializing HDF5 models.\n",
        "with tfmot.clustering.keras.cluster_scope():\n",
        "  loaded_model = tf.keras.models.load_model(keras_model_file)\n",
        "\n",
        "loaded_model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "cUv-scK-N4sN"
      },
      "source": [
        "## クラスタモデルの精度を改善する"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-fZZopDBN4sO"
      },
      "source": [
        "特定のユースケースについて、次のヒントを考慮できます。\n",
        "\n",
        "- 最終的な最適化モデルの精度には、重心の初期化に重要な役割があります。通常、線形初期化は大きな重みを見逃さない傾向があるため、密度とランダム初期化のパフォーマンスを上回ります。ただし、密度の初期化は、二峰性分布で重みに非常に少数のクラスタを使用した場合に、より優れた精度を示すことが確認されています。\n",
        "\n",
        "- クラスタモデルを微調整する際は、トレーニングに使用されている学習率よりも低い率を設定します。\n",
        "\n",
        "- モデルの精度を改善するための一般的なアイデアについては、「クラスタモデルを定義する」に記載のケース別のヒントをご覧ください。"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4DXw7YbyN4sP"
      },
      "source": [
        "## デプロイ"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5Y5zLfPzN4sQ"
      },
      "source": [
        "### サイズ圧縮によるモデルのエクスポート"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wX4OrHD9N4sQ"
      },
      "source": [
        "**一般的な過ち**: `strip_clustering` と標準圧縮アルゴリズム（gzip など）の適用は、クラスタリングの圧縮のメリットを確認する上で必要です。"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ZvuiCBsVN4sR"
      },
      "outputs": [],
      "source": [
        "model = setup_model()\n",
        "clustered_model = cluster_weights(model, **clustering_params)\n",
        "\n",
        "clustered_model.compile(\n",
        "    loss=tf.keras.losses.categorical_crossentropy,\n",
        "    optimizer='adam',\n",
        "    metrics=['accuracy']\n",
        ")\n",
        "\n",
        "clustered_model.fit(\n",
        "    x_train,\n",
        "    y_train\n",
        ")\n",
        "\n",
        "final_model = tfmot.clustering.keras.strip_clustering(clustered_model)\n",
        "\n",
        "print(\"final model\")\n",
        "final_model.summary()\n",
        "\n",
        "print(\"\\n\")\n",
        "print(\"Size of gzipped clustered model without stripping: %.2f bytes\" \n",
        "      % (get_gzipped_model_size(clustered_model)))\n",
        "print(\"Size of gzipped clustered model with stripping: %.2f bytes\" \n",
        "      % (get_gzipped_model_size(final_model)))"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "clustering_comprehensive_guide.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
